/**
 * Copyright 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package us.taxreceipt.chart.client;

import java.util.ArrayList;

import us.taxreceipt.chart.shared.FieldVerifier;

import com.google.gwt.ajaxloader.client.ArrayHelper;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayInteger;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DisclosurePanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

import com.google.gwt.visualization.client.DataView;
import com.google.gwt.visualization.client.LegendPosition;
import com.google.gwt.visualization.client.Query;
import com.google.gwt.visualization.client.QueryResponse;
import com.google.gwt.visualization.client.VisualizationUtils;
import com.google.gwt.visualization.client.DataTable;
import com.google.gwt.visualization.client.Selection;
import com.google.gwt.visualization.client.Query.Callback;
import com.google.gwt.visualization.client.events.SelectHandler;
import com.google.gwt.visualization.client.formatters.BarFormat;
import com.google.gwt.visualization.client.formatters.PatternFormat;
import com.google.gwt.visualization.client.formatters.BarFormat.Color;
import com.google.gwt.visualization.client.visualizations.BarChart;
import com.google.gwt.visualization.client.visualizations.PieChart;
import com.google.gwt.visualization.client.visualizations.Table;
import com.google.gwt.visualization.client.visualizations.PieChart.Options;
import com.google.gwt.visualization.client.visualizations.Table.Options.CssClassNames;

import com.google.gwt.i18n.client.NumberFormat;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 *
 * @author carterm@google.com (Carter Maslan)
 */
public class Taxreceipt implements EntryPoint {
	private double amount_paid = 0.0;
	/**
	 * The message displayed to the user when the server cannot be reached or
	 * returns an error.
	 */
	private static final String SERVER_ERROR = "An error occurred while "
			+ "attempting to contact the server. Please check your network "
			+ "connection and try again.";

	/**
	 * Create a remote service proxy to talk to the server-side Greeting service.
	 */
	private final GreetingServiceAsync greetingService = GWT
			.create(GreetingService.class);

	// federal budget pie options
	private Options createFederalPieOptions() {
	    Options options = Options.create();
	    options.setWidth(800);
	    options.setHeight(400);
	    options.set3D(true);		  
	    options.setLegend(LegendPosition.NONE);
	    return options;
	}
	
	// personal fed taxes paid pie options
	private Options createPaidPieOptions() {
		    Options options = Options.create();
		    options.setWidth(400);
		    options.setHeight(320);
		    options.set3D(true);		  
		    options.setLegend(LegendPosition.NONE);
		    return options;
		  }

	/* TODO(carterm) - link to more info on each budget item later
	 * 
		  private SelectHandler createSelectHandler(final PieChart chart) {
		    return new SelectHandler() {
		      @Override
		      public void onSelect(SelectEvent event) {
		        String message = "";
		        
		        // May be multiple selections.
		        JsArray<Selection> selections = chart.getSelections();

		        for (int i = 0; i < selections.length(); i++) {
		          // add a new line for each selection
		          message += i == 0 ? "" : "\n";
		          
		          Selection selection = selections.get(i);

		          if (selection.isCell()) {
		            // isCell() returns true if a cell has been selected.
		            
		            // getRow() returns the row number of the selected cell.
		            int row = selection.getRow();
		            // getColumn() returns the column number of the selected cell.
		            int column = selection.getColumn();
		            message += "cell " + row + ":" + column + " selected";
		          } else if (selection.isRow()) {
		            // isRow() returns true if an entire row has been selected.
		            
		            // getRow() returns the row number of the selected row.
		            int row = selection.getRow();
		            message += "row " + row + " selected";
		          } else {
		            // unreachable
		            message += "Pie chart selections should be either row selections or cell selections.";
		            message += "  Other visualizations support column selections as well.";
		          }
		        }
		        
		        Window.alert(message);
		      }
		    };
		  }
		  */

	private void createTables(String paid, String string) {
		if (paid == null) createFederalTable(string);
		else createPaidTable(paid);
	}
	
	  private void createPaidTable(String paid) {
		  if (paid == null) return;
		  NumberFormat fmt = NumberFormat.getDecimalFormat();	
		  try {
			  double dPaid = fmt.parse(paid);
			  createPaidTable(dPaid);
		  }
		  catch (NumberFormatException e) {
			  Label errorLabel = (Label) RootPanel.get("errorLabelContainer").getWidget(0);
			  errorLabel.setText("Enter the numeric amount you paid");
		  }
	  }
	  
	  
	  private void createPaidTable(double paid) {
		  amount_paid = paid;
		  if (amount_paid == 0.0) return;
		  Label errorLabel = (Label) RootPanel.get("errorLabelContainer").getWidget(0);
		  errorLabel.setText("Calculating your tax receipt...");
		  // read spreadsheet as data source: https://spreadsheets.google.com/ccc?key=0AvLHzX42FVNldFE2NDZtNTZDekxXdWxWVEhfS0xMX1E&hl=en#gid=0
		    String dataUrl = "https://spreadsheets0.google.com/tq?range=A1:F400&key=0AvLHzX42FVNldFE2NDZtNTZDekxXdWxWVEhfS0xMX1E&gid=0"; 
	
		      // Create a query to go to the above URL.
		      Query query = Query.create(dataUrl);
	
		      // Send the query.
		      query.send(new Callback() {
		        public void onResponse(QueryResponse response) {
		          if (response.isError()) {
		            Window.alert(response.getMessage());
		          } else {
		            // Get the data from the QueryResponse.
		            DataTable data = response.getDataTable();
		            
		            // calculate dollar amount paid and the set of row numbers with negative values to exclude from pie chart			            
		            ArrayList<Integer> negativeRows = new ArrayList<Integer>();
		            for (int i=0; i < data.getNumberOfRows(); i++) {
		            	double p = data.getValueDouble(i, 2);
		            	if (p < 0) negativeRows.add(i); // add the row number to the set of negative value rows
		            	double dollarValue = p * amount_paid;
		            	NumberFormat fmt = NumberFormat.getCurrencyFormat();	        	    			        	    
		        	    String dollarFormatted = fmt.format(dollarValue);
		            	data.setCell(i, 3, dollarValue, dollarFormatted, null);		            				        
		            }
		            data.setColumnLabel(3, "you paid");			           

		            Options options = createPaidPieOptions();
		            
		            // Create a PieChart and add it to a panel.
			        Panel panel = RootPanel.get("charts");
			   	 	panel.clear();				   	 					   	 
			   	 	
			   	 	// create the personal taxes paid pie
			   	 	DataView pieView = DataView.create(data);
			   	    pieView.setColumns(new int[]{1, 2});	
			   	    // hide the rows with negative values since it's a pie chart
			   	    for (int i=0; i < negativeRows.size(); i++) {
			   	    	pieView.hideRows(negativeRows.get(i), negativeRows.get(i));
			   	    }
			        PieChart pie = new PieChart(pieView, options);
			        
			        DisclosurePanel dp = new DisclosurePanel("Your Tax Pie");
			        dp.setTitle("See a pie chart of your taxes");
			        dp.add(pie);
			        
			        Table table = new Table();
			        Table.Options tableOptions = Table.Options.create();
			        tableOptions.setAlternatingRowStyle(true);
			        CssClassNames classNames = CssClassNames.createObject().cast();
			        classNames.setTableCell("receipt");
			        tableOptions.setCssClassNames(classNames);	
			        tableOptions.setAllowHtml(true);
			        
			        BarFormat.Options barOptions = BarFormat.Options.create();
			        barOptions.setColorNegative(Color.GREEN);				        
			        barOptions.setColorPositive(Color.RED);
			        barOptions.setWidth(150);
			        BarFormat formatter = BarFormat.create(barOptions);				        
			        formatter.format(data, 3);	
			        
			        /* TODO(carterm) - include links to more info for each item later
			        PatternFormat pf = PatternFormat.create("<a href='{4}'>{5}</a>");
			        int[] java = new int[]{4, 5};
			        JsArrayInteger js = ArrayHelper.toJsArrayInteger(java);
			        pf.format(data, js, 4);	
			        */
			        
			   	 	DataView tableView = DataView.create(data);
			   	 	tableView.setColumns(new int[]{0, 1, 3});				   	 
			   	 	tableOptions.setSortColumn(2);
			   	 	tableOptions.setSortAscending(false);
	
			        table.draw(tableView, tableOptions);
			        				        
			        Panel tablePanel = RootPanel.get("tables");
			        tablePanel.clear();
			        tablePanel.add(dp);
			        tablePanel.add(table);
			        Label errorLabel = (Label) RootPanel.get("errorLabelContainer").getWidget(0);
					errorLabel.setText("");
		          }
		        }
		      });			  
	  }	

	  private void createFederalTable(String taxYear) {		  
		  if (taxYear != "2010") return; // TODO(carterm) add lookup by year
		  Label errorLabel = (Label) RootPanel.get("errorLabelContainer").getWidget(0);
		  errorLabel.setText("Retrieving full Federal Budget...");
		  // read spreadsheet as data source: https://spreadsheets.google.com/ccc?key=0AvLHzX42FVNldFE2NDZtNTZDekxXdWxWVEhfS0xMX1E&hl=en#gid=6
		    String dataUrl = "https://spreadsheets0.google.com/tq?range=A1:C100&key=0AvLHzX42FVNldFE2NDZtNTZDekxXdWxWVEhfS0xMX1E&gid=6"; 
	
		      // Create a query to go to the above URL.
		      Query query = Query.create(dataUrl);
	
		      // Send the query.
		      query.send(new Callback() {
		        public void onResponse(QueryResponse response) {
		          if (response.isError()) {
		            Window.alert(response.getMessage());
		          } else {
		            // Get the data from the QueryResponse.
		            DataTable data = response.getDataTable();
		            
		            ArrayList<Integer> negativeRows = new ArrayList<Integer>();
		            for (int i=0; i < data.getNumberOfRows(); i++) {
		            	double p = data.getValueDouble(i, 2);
		            	if (p < 0) negativeRows.add(i); // add the row number to the set of negative value rows
		            }
		            // Create the Options object.			           
		            Options options = createFederalPieOptions();
				    options.setTitle("2010 Federal Budget Expenses");
		            
		            // Create a PieChart and add it to a panel.
			        Panel panel = RootPanel.get("charts");
			   	 	panel.clear();				   	 					   	 
			   	 	
			   	 	// create the federal budget pie
			   	 	DataView pieView = DataView.create(data);
			   	    pieView.setColumns(new int[]{0, 2});
			   	    for (int i=0; i < negativeRows.size(); i++) {
			   	    	pieView.hideRows(negativeRows.get(i), negativeRows.get(i));
			   	    }
			        PieChart pie = new PieChart(pieView, options);
			        panel.add(pie);

			        Table table = new Table();
			        Table.Options tableOptions = Table.Options.create();
			        tableOptions.setAlternatingRowStyle(true);
			        CssClassNames classNames = CssClassNames.createObject().cast();
			        classNames.setTableCell("receipt");
			        tableOptions.setCssClassNames(classNames);	
			        tableOptions.setAllowHtml(true);
			        
			        BarFormat.Options barOptions = BarFormat.Options.create();
			        barOptions.setColorNegative(Color.GREEN);				        
			        barOptions.setColorPositive(Color.RED);
			        barOptions.setWidth(250);
			        BarFormat formatter = BarFormat.create(barOptions);				        
			        formatter.format(data, 2);	
			        
			        /*
			        PatternFormat pf = PatternFormat.create("<a href='{4}'>{5}</a>");
			        int[] java = new int[]{4, 5};
			        JsArrayInteger js = ArrayHelper.toJsArrayInteger(java);
			        pf.format(data, js, 4);	
			        */
			        
			   	 	DataView tableView = DataView.create(data);
			   	 	tableView.setColumns(new int[]{0, 2});				   	 
			   	 	tableOptions.setSortColumn(1);
			   	 	tableOptions.setSortAscending(false);
	
			        table.draw(tableView, tableOptions);
			        				        
			        Panel tablePanel = RootPanel.get("tables");
			        tablePanel.clear();
			        tablePanel.add(table);
			        
			        Label errorLabel = (Label) RootPanel.get("errorLabelContainer").getWidget(0);
					errorLabel.setText("");
		          }
		        }
		      });			  
	  }	

	  
	
	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
	    // Create a callback to be called when the visualization API
	    // has been loaded.
  	  	final String fedtax = Window.Location.getParameter("fedtax");
	    Runnable onLoadCallback = new Runnable() {
	      public void run() {
	    	  createTables(fedtax, "2010");
	      }
	    };

		final Button calcButton = new Button("Calculate");
		final TextBox fedTaxPaidField = new TextBox();
		fedTaxPaidField.setWidth("9em");
		fedTaxPaidField.setStylePrimaryName("amountPaid");
		
		if (fedtax == null)
			fedTaxPaidField.setText("10,000");
		else
			fedTaxPaidField.setText(fedtax);
		
		final Label errorLabel = new Label();

		calcButton.addStyleName("calcButton");

		RootPanel.get("fedTaxPaidContainer").add(fedTaxPaidField);
		RootPanel.get("calcButtonContainer").add(calcButton);
		RootPanel.get("errorLabelContainer").add(errorLabel);

		// Focus the cursor on the name field when the app loads
		fedTaxPaidField.setFocus(true);
		fedTaxPaidField.selectAll();

	    // Load the visualization api, passing the onLoadCallback to be called
	    // when loading is done.
	    VisualizationUtils.loadVisualizationApi(onLoadCallback, PieChart.PACKAGE, Table.PACKAGE, BarChart.PACKAGE);
						
		
		// Create the popup dialog box
		final DialogBox dialogBox = new DialogBox();
		dialogBox.setText("Remote Procedure Call");
		dialogBox.setAnimationEnabled(true);
		final Button closeButton = new Button("Close");
		// We can set the id of a widget by accessing its Element
		closeButton.getElement().setId("closeButton");
		final Label textToServerLabel = new Label();
		final HTML serverResponseLabel = new HTML();
		VerticalPanel dialogVPanel = new VerticalPanel();
		dialogVPanel.addStyleName("dialogVPanel");
		dialogVPanel.add(new HTML("<b>Sending amount to the server:</b>"));
		dialogVPanel.add(textToServerLabel);
		dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
		dialogVPanel.add(serverResponseLabel);
		dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
		dialogVPanel.add(closeButton);
		dialogBox.setWidget(dialogVPanel);

		// Add a handler to close the DialogBox
		closeButton.addClickHandler(new ClickHandler() {
			public void onClick(ClickEvent event) {
				dialogBox.hide();
				calcButton.setEnabled(true);
				calcButton.setFocus(true);
			}
		});

		// Create a handler for the calcButton and nameField
		class MyHandler implements ClickHandler, KeyUpHandler {
			/**
			 * Fired when the user clicks on the calcButton.
			 */
			public void onClick(ClickEvent event) {
				sendPaidToServer();
			}

			/**
			 * Fired when the user types in the nameField.
			 */
			public void onKeyUp(KeyUpEvent event) {
				if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
					sendPaidToServer();
				}
			}

			/**
			 * Send the name from the nameField to the server and wait for a response.
			 */
			private void sendPaidToServer() {
				// First, we validate the input.
				errorLabel.setText("");
				String textToServer = fedTaxPaidField.getText();
				if (!FieldVerifier.isValidAmount(textToServer)) {
					errorLabel.setText("First, enter the amount you paid");
					return;
				}
				createPaidTable(textToServer);

				/* TODO(carterm) - not using RPC to server for now, since client reads directly from spreadsheet instead.
				calcButton.setEnabled(false);
				textToServerLabel.setText(textToServer);
				serverResponseLabel.setText("");
				greetingService.greetServer(textToServer,
						new AsyncCallback<String>() {
							public void onFailure(Throwable caught) {
								// Show the RPC error message to the user
								dialogBox
										.setText("Remote Procedure Call - Failure");
								serverResponseLabel
										.addStyleName("serverResponseLabelError");
								serverResponseLabel.setHTML(SERVER_ERROR);
								dialogBox.center();
								closeButton.setFocus(true);
							}

							public void onSuccess(String result) {
								dialogBox.setText("Remote Procedure Call");
								serverResponseLabel
										.removeStyleName("serverResponseLabelError");
								serverResponseLabel.setHTML(result);
								dialogBox.center();
								closeButton.setFocus(true);								
							}
						});
				*/
			}
		}

		// Add a handler to send the name to the server
		MyHandler handler = new MyHandler();
		calcButton.addClickHandler(handler);
		fedTaxPaidField.addKeyUpHandler(handler);
	}
}
